<template>
  <view class="detail-container" v-if="detail">
    <!-- 顶部动态安全占位，按设备状态栏/安全区高度计算，避免被遮挡 -->
    <view class="safe-top" :style="{ height: safeTop + 'px' }"></view>
    <!-- 自定义返回按钮（左上角） -->
    <view class="nav-back" @click="goBack">
      <text class="nav-back-icon">←</text>
    </view>
    <view class="header-card" :style="{ paddingRight: capsulePad + 'px' }">
      <image class="avatar" :src="detail.avatar" mode="aspectFill" />
      <view class="base-info">
        <view class="name">{{ detail.name }}</view>
        <view class="meta">
          <text class="role" v-if="detail.title">{{ detail.title }}</text>
          <text class="divider" v-if="detail.title && detail.hospital">｜</text>
          <text class="hospital" v-if="detail.hospital">{{ detail.hospital }}</text>
        </view>
        <view class="stats">
          <text>服务患者数 {{ detail.consultationCount }}</text>
          <text class="dot">·</text>
          <text>好评率 {{ displayPraiseRate }}</text>
        </view>
      </view>
      <!-- 右侧预约咨询区域，避免与胶囊重叠 -->
      <view class="consult-area">
        <button class="consult-btn" @tap="goConsult">
          <text>预约咨询</text>
          <view class="price" v-if="detail.price">{{ detail.price }}币/次</view>
        </button>
        <button class="follow-btn" :class="{ active: isFollowed }" @click="toggleFollow">
          {{ isFollowed ? '已关注' : '关注' }}
        </button>
      </view>
    </view>

    <view class="section" v-if="detail.specialty">
      <view class="section-title">擅长</view>
      <view class="section-content">{{ detail.specialty }}</view>
    </view>

    <view class="section" v-if="detail.intro">
      <view class="section-title">简介</view>
      <view class="section-content">{{ detail.intro }}</view>
    </view>

    <!-- 用户评价 -->
    <view class="section">
      <view class="section-title">用户评价 <text class="sub">（{{ evaluations.length }}条评论）</text></view>
      <view v-if="evaluations.length === 0" class="empty">暂无评价</view>
      <view v-else class="evaluation-list">
        <view class="evaluation-item" v-for="item in visibleEvaluations" :key="item.id">
          <image class="ev-avatar" :src="item.userAvatar" mode="aspectFill" />
          <view class="ev-content">
            <view class="ev-header">
              <text class="ev-name">{{ item.userName }}</text>
              <text class="ev-time">{{ item.timeText }}</text>
            </view>
            <view class="ev-scores" v-if="item.professionalScore || item.serviceScore">
              <view class="score-line" v-if="item.professionalScore">
                <text class="label">专业评分</text>
                <view class="stars">
                  <text v-for="i in 5" :key="'p'+i" class="star" :class="{ active: i <= item.professionalScore }">★</text>
                </view>
                <text class="score-number">{{ item.professionalScore }}分</text>
              </view>
              <view class="score-line" v-if="item.serviceScore">
                <text class="label">服务评分</text>
                <view class="stars">
                  <text v-for="i in 5" :key="'s'+i" class="star" :class="{ active: i <= item.serviceScore }">★</text>
                </view>
                <text class="score-number">{{ item.serviceScore }}分</text>
              </view>
            </view>
            <view class="ev-text">{{ item.content }}</view>
          </view>
        </view>
      </view>
      <view class="more-area" v-if="evaluations.length > 2">
        <text class="more-btn" @click="toggleEvaluations">{{ showAllEvaluations ? '收起评论' : '点击查看更多评论' }}</text>
      </view>
    </view>
  </view>
  <view v-else class="loading">加载中...</view>
</template>

<script>
import { getDoctorDetail } from '@/api/doctor'
import { getPatientEvaluations } from '@/api/patientEvaluation'
import { listMyFollow, addFollow, cancelFollow } from '@/api/follow'
import { startConsult, goChat } from '@/api/consult'
import config from '@/config.js'
export default {
  data() {
    return {
      doctorId: null,
      detail: null,
      evaluations: [],
      avgProfessional: 0,
      avgService: 0,
      // 评论展开/收起
      showAllEvaluations: false,
      // 顶部安全占位高度（px）
      safeTop: 0,
      // 小程序右上角胶囊避让 padding（px）
      capsulePad: 0,
      // 关注状态
      isFollowed: false
    }
  },
  async onLoad(options) {
    this.doctorId = options && options.doctorId ? Number(options.doctorId) : null
    if (!this.doctorId) {
      this.$modal && this.$modal.msg('缺少医生ID')
      return
    }
    // 初始关注状态，优先使用路由参数，其次查询关注列表
    if (options && (options.isFollowed === '1' || options.isFollowed === 'true')) {
      this.isFollowed = true
    } else if (options && (options.isFollowed === '0' || options.isFollowed === 'false')) {
      this.isFollowed = false
    } else {
      try {
        const resF = await listMyFollow()
        const arr = Array.isArray(resF?.data) ? resF.data : (Array.isArray(resF) ? resF : [])
        this.isFollowed = (arr || []).some(it => Number(it.doctorId || it.doctor_id || it.docId) === this.doctorId)
      } catch {}
    }
    // 依据设备信息动态计算顶部安全占位，优先使用 safeAreaInsets.top / statusBarHeight
    try {
      const info = uni.getSystemInfoSync()
      const insetTop = (info.safeAreaInsets && info.safeAreaInsets.top) || 0
      const bar = info.statusBarHeight || 0
      // 取更大的值，并设置一个保守下限 48px，再加 12px 视觉余量
      this.safeTop = Math.max(insetTop, bar, 48) + 12
    } catch (e) {
      // 失败则提供一个保守余量
      this.safeTop = 60
    }
    try {
      await Promise.all([
        this.fetchDetail(),
        this.fetchEvaluations()
      ])
    } catch (e) {
      // 单个方法内部已有捕获，这里兜底避免未处理异常阻断页面
      console.error('初始化数据失败', e)
    }

    // #ifdef MP-WEIXIN
    try {
      const info = uni.getSystemInfoSync()
      const menu = uni.getMenuButtonBoundingClientRect && uni.getMenuButtonBoundingClientRect()
      if (menu && info && info.windowWidth) {
        // 使用 menu.right 计算右侧剩余空间，避免过大的 padding 挤压内容
        const rawPad = info.windowWidth - menu.right + 8 // 额外 8px 视觉余量
        // 限制在 8~40px 之间，既避让胶囊又不影响布局
        const pad = Math.max(8, Math.min(rawPad, 40))
        this.capsulePad = pad
      }
    } catch (e) {
      this.capsulePad = 12
    }
    // #endif
  },
  computed: {
    visibleEvaluations() {
      return this.showAllEvaluations ? this.evaluations : this.evaluations.slice(0, 2)
    },
    displayPraiseRate() {
      const v = this.detail && this.detail.praiseRate
      if (v !== undefined && v !== null && v !== '') {
        return this.formatPercent(v)
      }
      if (this.evaluations && this.evaluations.length) {
        return this.computePraiseRateFromEvaluations(this.evaluations)
      }
      return '0%'
    }
  },
  methods: {
    async goConsult() {
      if (!this.doctorId) {
        this.$modal && this.$modal.msg('缺少医生ID')
        return
      }
      const name = (this.detail && this.detail.name) || '该'
      const priceText = (this.detail && (this.detail.price || this.detail.price === 0)) ? `${this.detail.price}币/次` : ''
      // 弹出确认，避免误触直接发起问诊
      uni.showModal({
        title: '确认预约',
        content: `将向「${name} 医生」发起预约问诊${priceText ? '（' + priceText + '）' : ''}，是否继续？`,
        cancelText: '再想想',
        confirmText: '确认预约',
        success: async (res) => {
          if (res.confirm) {
            try {
              console.log('[consult] startConsult -> doctorId:', this.doctorId)
              uni.showLoading({ title: '发起问诊' })
              const res2 = await startConsult(this.doctorId)
              console.log('[consult] startConsult result:', res2)
              const data = res2 && (res2.data || res2) || {}
              const sessionId = data.sessionId || (data.data && data.data.sessionId)
              uni.hideLoading()
              uni.showToast({ title: '问诊已开始', icon: 'none' })
              // 携带会话ID与医生信息（用于聊天页展示头像/标题）
              goChat({
                doctorId: this.doctorId,
                sessionId,
                doctorName: (this.detail && this.detail.name) || '',
                doctorAvatar: (this.detail && this.detail.avatar) || ''
              })
            } catch (e) {
              console.error('[consult] startConsult error:', e)
              uni.hideLoading()
              const msg = (e && (e.msg || e.message)) || '发起问诊失败'
              uni.showToast({ title: msg, icon: 'none' })
            }
          } else {
            // 用户取消
            console.log('[consult] user cancelled')
          }
        }
      })
    },
    async toggleFollow() {
      if (!this.doctorId) return
      try {
        uni.showLoading({ title: '处理中' })
        if (this.isFollowed) {
          await cancelFollow(this.doctorId)
          this.isFollowed = false
          uni.showToast({ title: '已取消关注', icon: 'none' })
        } else {
          await addFollow(this.doctorId)
          this.isFollowed = true
          uni.showToast({ title: '已关注', icon: 'none' })
        }
        // 广播给列表页
        uni.$emit && uni.$emit('follow-changed', { doctorId: this.doctorId, followed: this.isFollowed, when: Date.now() })
      } catch (e) {
        console.error('更新关注状态失败', e)
        uni.showToast({ title: '操作失败', icon: 'none' })
      } finally {
        uni.hideLoading()
      }
    },
    goBack() {
      // 优先返回上一页，否则跳转到医生列表
      const pages = getCurrentPages && getCurrentPages()
      if (pages && pages.length > 1) {
        uni.navigateBack({ delta: 1 })
      } else {
        uni.switchTab ? uni.switchTab({ url: '/pages/index' }) : uni.reLaunch({ url: '/pages/index' })
      }
    },
    async fetchDetail() {
      try {
        const res = await getDoctorDetail(this.doctorId)
        const d = res && (res.data || res) || {}
        const ratingNum = parseFloat(d.praiseRate !== undefined ? d.praiseRate : d.praise_rate)
        const priceNum = parseFloat(d.consultFee)
        const serviceNum = parseInt(d.serviceCount)
        const rawAvatar = d.doctorImage || d.avatar || d.headImg || d.avatarUrl
        const avatar = this.normalizeUrl(rawAvatar)
        this.detail = {
          id: d.id || d.doctorId || d.userId,
          name: d.doctorName || d.name || d.realName || '医生',
          title: d.roleName || d.title || d.jobTitle || '医师',
          hospital: d.hospitalName || d.hospital || '',
          avatar: avatar || '/static/images/profile.jpg',
          isOnline: d.isOnline === true || d.online === true || false,
          consultationCount: Number.isNaN(serviceNum) ? (d.consultationCount || d.serviceNum || 0) : serviceNum,
          rating: Number.isNaN(ratingNum) ? (d.rating || 98) : Math.round(ratingNum),
          praiseRate: Number.isNaN(ratingNum) ? '' : ratingNum,
          price: Number.isNaN(priceNum) ? (d.price || d.consultPrice || 0) : priceNum,
          // 映射 SQL 返回的更多字段名
          specialty: d.specialty || d.goodAt || d.skilled || d.expertise || '',
          intro: d.intro || d.profile || d.brief || d.introduction || d.desc || d.description || ''
        }
        uni.setNavigationBarTitle({ title: this.detail.name + ' 医生' })
      } catch (e) {
        console.error('获取医生详情失败', e)
        this.$modal && this.$modal.msg('获取医生详情失败')
      }
    },
    async fetchEvaluations() {
      try {
        const res = await getPatientEvaluations(this.doctorId)
        const list = Array.isArray(res) ? res : (res.data || [])
        this.evaluations = (list || []).map((it, idx) => {
          const when = it.evaluationTime || it.createTime || it.time || it.commentTime || ''
          // 兼容后端返回 patient_name 与 avatar 字段
          const name = it.patientName || it.patient_name || it.name || ''
          const avatarRaw = it.patientAvatar || it.patient_avatar || it.avatar || ''
          return {
            id: it.id || it.evaluationId || idx,
            userName: name,
            userAvatar: this.normalizeUrl(avatarRaw),
            content: it.evaluationContent || it.content || it.comment || it.remark || '',
            timeText: this.formatTime(when),
            professionalScore: Number(it.professionalScore || it.professionScore || 0),
            serviceScore: Number(it.serviceScore || 0)
          }
        })
        // 计算平均分
        const ps = this.evaluations.map(e => e.professionalScore).filter(n => n > 0)
        const ss = this.evaluations.map(e => e.serviceScore).filter(n => n > 0)
        const avg = (arr) => arr.length ? Math.round((arr.reduce((a,b)=>a+b,0) / arr.length) * 10) / 10 : 0
        this.avgProfessional = avg(ps)
        this.avgService = avg(ss)
      } catch (e) {
        console.error('获取患者评价失败', e)
      }
    },
    toggleEvaluations() {
      this.showAllEvaluations = !this.showAllEvaluations
    },
    // 计算好评率：平均分>=4 视为好评
    computePraiseRateFromEvaluations(list) {
      if (!Array.isArray(list) || list.length === 0) return '0%'
      const total = list.length
      const good = list.reduce((acc, it) => {
        const p = Number(it.professionalScore || 0)
        const s = Number(it.serviceScore || 0)
        const avg = (p + s) / 2
        return acc + (avg >= 4 ? 1 : 0)
      }, 0)
      const percent = Math.round((good / total) * 100)
      return `${percent}%`
    },
    // 规范化百分比展示：支持 0.95 / 95 / '95%'
    formatPercent(v) {
      if (v === null || v === undefined || v === '') return '0%'
      let str = String(v).trim()
      if (str.endsWith('%')) {
        const n = parseFloat(str.slice(0, -1))
        if (isNaN(n)) return '0%'
        const clamped = Math.max(0, Math.min(n, 100))
        return Math.round(clamped) + '%'
      }
      const num = parseFloat(str)
      if (isNaN(num)) return '0%'
      let percent = (num > 0 && num <= 1) ? num * 100 : num
      percent = Math.max(0, Math.min(percent, 100))
      return Math.round(percent) + '%'
    },
    normalizeUrl(url) {
      if (!url) return ''
      if (/^https?:\/\//i.test(url)) return url
      if (url.startsWith('/')) return `${config.baseUrl}${url}`
      return url
    },
    formatTime(ts) {
      if (!ts) return ''
      try {
        const d = new Date(ts)
        const y = d.getFullYear()
        const m = `${d.getMonth()+1}`.padStart(2,'0')
        const day = `${d.getDate()}`.padStart(2,'0')
        return `${y}.${m}.${day}`
      } catch {
        return ts
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.detail-container {
  background-color: #f5f5f5;
  min-height: 100vh;
  padding: 20rpx;
  /* 兼容小程序/iOS 刘海安全区，避免顶部元素被遮挡 */
  padding-top: calc(20rpx + constant(safe-area-inset-top));
  padding-top: calc(20rpx + env(safe-area-inset-top));
  /* 底部 Home 指示条安全区，避免底部按钮被遮挡 */
  padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
  box-sizing: border-box;
}
.header-card {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  background: #fff;
  border-radius: 20rpx;
  padding: 28rpx;
  box-shadow: 0 6rpx 16rpx rgba(0,0,0,0.08);
  /* 首屏额外下移，进一步避免被系统 UI 遮挡 */
  margin-top: 16rpx;
}
.avatar {
  width: 140rpx;
  height: 140rpx;
  border-radius: 70rpx;
  margin-right: 24rpx;
}
.base-info { flex: 1; min-width: 0; }
.name { font-size: 32rpx; font-weight: 600; color: #333; margin-bottom: 8rpx; }
.meta { display: flex; align-items: center; gap: 8rpx; font-size: 24rpx; color: #666; margin-bottom: 10rpx; overflow: hidden; }
.meta .role { color: #667eea; }
.meta .divider { color: #bbb; }
.meta .hospital { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stats { display: flex; gap: 24rpx; font-size: 22rpx; color: #9aa0a6; }
.score-summary { margin-top: 8rpx; display: flex; gap: 24rpx; align-items: center; flex-wrap: wrap; }
.score-summary .line { display:flex; align-items:center; gap: 8rpx; }
.score-summary .label { font-size: 22rpx; color:#666; }
.score-summary .stars { display:inline-flex; }
.score-summary .star { font-size: 22rpx; color:#e0e0e0; }
.score-summary .star.active { color:#f5a623; }
.score-summary .num { font-size: 22rpx; color:#999; }
.consult-area { flex-shrink: 0; width: 220rpx; display:flex; justify-content:flex-end; }
.consult-btn {
  background: #5b7bff;
  color: #fff;
  border: none;
  border-radius: 16rpx;
  padding: 20rpx 24rpx;
  font-size: 28rpx;
  line-height: 1;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6rpx 16rpx rgba(91,123,255,0.25);
}
.consult-area { margin-left: 20rpx; }
.price { margin-top: 6rpx; font-size: 22rpx; opacity: 0.9; }

.follow-btn {
  margin-top: 10rpx;
  background: #fff;
  color: #1677ff;
  border: 1rpx solid #1677ff;
  border-radius: 16rpx;
  padding: 14rpx 18rpx;
  font-size: 26rpx;
}
.follow-btn.active {
  background: #e6f0ff;
  color: #1677ff;
  border-color: #a3c6ff;
}

.section { margin-top: 24rpx; background:#fff; border-radius: 16rpx; padding: 24rpx; }
.section-title { font-size: 28rpx; font-weight: 600; color:#333; margin-bottom: 12rpx; }
.section-title .sub { font-size: 24rpx; color: #999; font-weight: 400; }
.section-content { font-size: 26rpx; color:#666; line-height: 1.6; }

.evaluation-list { display: flex; flex-direction: column; gap: 0; }
.evaluation-item { display: flex; padding-bottom: 20rpx; margin-bottom: 20rpx; border-bottom: 1rpx solid #eee; }
.ev-avatar { width: 72rpx; height: 72rpx; border-radius: 36rpx; margin-right: 16rpx; }
.ev-content { flex: 1; }
.ev-header { display:flex; align-items:center; justify-content:space-between; margin-bottom: 6rpx; }
.ev-name { font-size: 26rpx; color:#333; font-weight: 500; }
.ev-date { font-size: 22rpx; color:#999; white-space: nowrap; margin-left: 12rpx; }
.ev-header { display: flex; justify-content: space-between; margin-bottom: 6rpx; }
.ev-name { font-size: 26rpx; color:#333; }
.ev-time { font-size: 22rpx; color:#999; }
.ev-text { font-size: 26rpx; color:#444; line-height: 1.6; }
.empty { color:#999; font-size:24rpx; }

.ev-scores { margin-bottom: 6rpx; }
.score-line { display: flex; align-items: center; gap: 10rpx; margin: 4rpx 0; }
.score-line .label { font-size: 22rpx; color: #666; }
.stars { display: inline-flex; }
.star { font-size: 22rpx; color: #e0e0e0; }
.star.active { color: #f5a623; }
.score-number { font-size: 22rpx; color: #999; }

.more-area { margin-top: 8rpx; text-align: center; }
.more-btn { color: #1677ff; font-size: 26rpx; }

.loading { padding: 60rpx; text-align:center; color:#999; }

/* 顶部动态安全占位，不占据视觉元素，仅用于避免遮挡 */
.safe-top { width: 100%; }

/* 左上角返回按钮 */
.nav-back {
  position: fixed;
  left: 12px;
  top: 8px;
  z-index: 999;
  width: 34px;
  height: 34px;
  border-radius: 17px;
  background: rgba(255,255,255,0.92);
  box-shadow: 0 4px 12px rgba(0,0,0,0.12);
  display: flex;
  align-items: center;
  justify-content: center;
}
.nav-back-icon { color: #333; font-size: 18px; line-height: 1; }
</style>
